package scuts.core;

import scuts.core.types.LazyIterator;

class DropArray
{
  // override Iterable for performance
  public static inline function dropToArray<T>(a:Array<T>, num:Int):Array<T> {
    return drop(a, num);
  }
  public static function drop<T>(a:Array<T>, num:Int):Array<T>
  {
    var res = [];
    var i = 0;
    while (i < num && i < a.length) {
      i++;
    }
    while (i < a.length) {
      res.push(a[i]);
      i++;
    }
    return res;
    
  }
  
  public static function dropWhileWithIndex<T>(a:Array<T>, f:T->Int->Bool):Array<T>
  {
    var res = [];
    var i = 0;
    while (i < a.length && f(a[i], i)) {
      i++;
    }
    while (i < a.length) {
      res.push(a[i]);
      i++;
    }
    return res;
    
  }
  
  public static function dropWhile<T>(a:Array<T>, f:T->Bool):Array<T>
  {
    var res = [];
    var i = 0;
    while (i < a.length && f(a[i])) {
      i++;
    }
    while (i < a.length) {
      res.push(a[i]);
      i++;
    }
    return res;
    
  }
  
}

class DropIterable
{

  public static function dropToArray<T>(a:Iterable<T>, num:Int):Array<T>
  {
    var res = [];
    var iter = a.iterator();
    var i = 0;
    while (i < num && iter.hasNext()) {
      
      iter.next();
      i++;
    }
    while (iter.hasNext()) {
      res.push(iter.next());
    }
    return res;
    
  }
  
  public static function dropToList<T>(a:Iterable<T>, num:Int):List<T>
  {
    var res = new List();
    var iter = a.iterator();
    var i = 0;
    while (i < num && iter.hasNext()) {
      
      iter.next();
      i++;
    }
    while (iter.hasNext()) {
      res.add(iter.next());
    }
    return res;
    
  }
  
  public static function dropWhileToArrayWithIndex<T>(a:Iterable<T>, f:T->Int->Bool):Array<T>
  {
    var res = [];
    var iter = a.iterator();
    var i = 0;
    while (iter.hasNext()) {
      
      var cur = iter.next();
      
      if (!f(cur, i)) {
        res.push(cur);
        break;
      }
      i++;
    }
    while (iter.hasNext()) {
      res.push(iter.next());
    }
    return res;
    
  }
  
  public static function dropWhileToArray<T>(a:Array<T>, f:T->Bool):Array<T>
  {
    var res = [];
    var iter = a.iterator();
    while (iter.hasNext()) 
    {
      var cur = iter.next();
      
      if (!f(cur)) {
        res.push(cur);
        break;
      }
    }
    while (iter.hasNext()) {
      res.push(iter.next());
    }
    return res;
  }
}

class DropIterator {
  public static function drop<T>(a:Iterator<T>, num:Int):Iterator<T>
  {
    var res = [];
    var i = 0;
    var cur;
    
    return {
      hasNext : function () {
        while (i < num && a.hasNext()) {
          a.next();
          i++;
        }
        if (i < num) i = num;
        return a.hasNext();
      },
      next : function () {
        return a.next();
      }
    }
  }
}

class DropLazyIterator {
  public static function drop<T>(a:LazyIterator<T>, num:Int):LazyIterator<T>
  {
    return function () return DropIterator.drop(a(), num);
  }
}